﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using System.Data;
using System.Xml.Serialization;
using System.Security.Principal;
using System.Web.Services.Protocols;
using SharePoint.Dws;
using SharePoint.Lists;
using SharePoint.UserGroup;

namespace VTFC
{
    // ----------------------------------------------------------------------------
    /// <summary>
    /// TFS connector class.
    /// </summary>
    /// <remarks>
    /// Integration is implemented using WSS 3.0 web services.
    /// </remarks>
    // ----------------------------------------------------------------------------
    public class Connector
    {
        /// <summary>List Data Retrieval Web Service proxy instance.</summary>
        private static volatile Lists lists = null;
        /// <summary>Document Workspace Web Service  proxy instance.</summary>
        private static volatile Dws dws = null;
        /// <summary>UserGroup Web Service  proxy instance.</summary>
        private static volatile UserGroup usergroup = null;

        /// <summary>Is the connector initialized?</summary>
        private static bool connected = false;
        /// <summary>If true, connect, disconnect does not appear!</summary>
        private static bool batchcommand = false;

        /// <summary>Helper object for locking public methods.</summary>
        private static object olock = new object();        
        
        /// <summary>Container for <see cref="VTFC.Task"/> instances.</summary>
        private static List<Task> tasks = null;
        /// <summary>Contains all server urls for checking deleted documents.</summary>
        private static List<string> documenturls = null;
        /// <summary>Contains all server urls for checking deleted folders.</summary>
        private static List<string> folderurls = null;
        /// <summary>Contains all server urls for checking deleted lists.</summary>
        private static List<string> listurls = null;
        /// <summary><see cref="VTFC.Server"/> instance.</summary>
        private static Server server = null;

        /// <summary><see cref="VTFC.Content"/> of the current project.</summary>
        public static Content Content { get; set; }
        /// <summary>The current user technical identifier at TFS.</summary>
        private static int userID = -1;
        /// <summary>Connection timeout parameter.</summary>
        private static int timeout = 300;

        // ----------------------------------------------------------------------------
        /// <summary>
        /// The connected <see cref="VTFC.Server"/> instance.
        /// </summary>
        // ----------------------------------------------------------------------------        
        public static Server Server
        {
            get { return server; }
        }//prop Server

        // ----------------------------------------------------------------------------
        /// <summary>
        /// The current user technical identifier at TFS.
        /// </summary>
        // ----------------------------------------------------------------------------        
        public static int UserID
        {
            get { return userID; }
        }//prop UserID

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Is the connector initialized?
        /// </summary>
        // ----------------------------------------------------------------------------
        public static bool Connected
        {
            get { return connected; }
        }//prop Connected

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Constructor.
        /// </summary>
        // ----------------------------------------------------------------------------
        static Connector()
        {
        }//ctor
                        
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Initialize the connector.
        /// </summary>
        /// <param name="s"><see cref="VTFC.Server"/> instance.</param>
        // ----------------------------------------------------------------------------
        public static void Initialize(Server s)
        {
            lock (olock)
            {
                connected = false;
                server = s;
                timeout = ConfigManager.GetInt32(Constant.VTFC_CONFIGURATION_CONNECTIONTIMEOUT);
                string contentpath = Path.Combine(server.WorkDircetory, Constant.VTFC_CONTENT_FILE);
                if (File.Exists(contentpath))
                {
                    Content = LoadContent(contentpath);
                    if (Content != null)
                    {
                        CleanContent();
                    }
                    else
                    {
                        Content = new Content();
                    }
                }
                else
                {
                    Content = new Content();
                }
                if (Content.Lists == null)
                {
                    Content.Lists = new List<List>();
                }
                if (Content.Documents == null)
                {
                    Content.Documents = new List<Document>();
                }                
                if (Content.Icons == null)
                {
                    Content.Icons = new List<Icon>();
                }

                LoadCurrentUserID();

                connected = true;
            }
        }//func Initialize

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Get TFS server url from a Content.tfc XML.
        /// </summary>
        /// <param name="contentpath">Content.tfc path.</param>
        /// <returns>ServerUrl</returns>
        // ----------------------------------------------------------------------------
        public static string GetContentServerUrl(string contentpath)
        {
            lock (olock)
            {
                string serverurl = null;
                Content content = LoadContent(contentpath);
                if (content != null)
                {
                    serverurl = content.ServerUrl;
                }
                return serverurl;
            }
        }//func GetContentServerUrl
              
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Order and save Content.tfc XML file to the workdirectory of current server.
        /// </summary>
        // ----------------------------------------------------------------------------
        public static void SaveContent()
        {
            lock (olock)
            {
                if (server != null)
                {
                    Content.ServerUrl = server.Url;
                    Content.Order();

                    string contentpath = Path.Combine(server.WorkDircetory, Constant.VTFC_CONTENT_FILE);

                    BaseLogic.CleanPath(contentpath);

                    using (MemoryStream ms = new MemoryStream())
                    {                        
                        XmlSerializer serializer = new XmlSerializer(typeof(VTFC.Content));
                        serializer.Serialize(ms, Content);
                        File.WriteAllBytes(contentpath, ms.ToArray());
                    }
                }
            }
        }//func SaveContent

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Load the current user technical identifier to the UserID property.
        /// </summary>
        // ----------------------------------------------------------------------------
        public static void LoadCurrentUserID()
        {
            lock (olock)
            {
                string loginname = GetCurrentLoginName();
                try
                {                    
                    Connect();
                    XmlNode node = usergroup.GetUserInfo(loginname);
                    DataSet ds = GetXmlInDataSet(node);
                    userID = Int32.Parse(ds.Tables["User"].Rows[0]["ID"].ToString());
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
            }
        }//func LoadCurrentUserID

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Get all document library from TFS.
        /// </summary>
        /// <returns><see cref="List"/> list.</returns>
        // ----------------------------------------------------------------------------
        public static List<List> GetDocumentLibraryList()
        {
            lock (olock)
            {
                List<List> lists = null;
                try
                {
                    Connect();
                    lists = GetLists();
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return lists;
            }
        }//func GetDocumentLibraryList
                
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Collect the task list for connected server.
        /// </summary>
        /// <returns><see cref="VTFC.Task"/> list.</returns>
        // ----------------------------------------------------------------------------
        public static List<Task> GetTaskList()
        {
            lock (olock)
            {
                tasks = new List<Task>();
                try
                {
                    Connect();
                    List<List> tfslists = GetLists();

                    listurls = new List<string>();
                    folderurls = new List<string>();
                    documenturls = new List<string>();
                    List list = null;
                    foreach (List tfslist in tfslists)
                    {
                        listurls.Add(tfslist.Url);
                        list = Content.GetListByName(tfslist.Name);
                        if (list == null)
                        {
                            list = new List()
                            {
                                Directory = tfslist.Directory,
                                Folders = new List<Folder>(),
                                ID = tfslist.ID,
                                Name = tfslist.Name,
                                Url = tfslist.Url,
                            };
                            Content.Lists.Add(list);
                        }
                        GetFolders(list, list.Directory);
                    }

                    CleanDocuments();
                    CleanLists();
                }
                catch ( SoapException ex )
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return tasks;
            }
        }//func GetTaskList
                        
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Execute a <see cref="VTFC.Task"/>.
        /// </summary>
        /// <param name="task"><see cref="VTFC.Task"/> instance.</param>
        // ----------------------------------------------------------------------------
        public static void ExecuteTask(Task task)
        {
            lock (olock)
            {               
                Document document = task.Document;
                DownloadDocument(task.Document.Url, task.Path);
                
                document.MetaData = task.MetaData;
                document.Skipped = false;
                task.Completed = true;
                
                if (!Content.Documents.Contains(document))
                {
                    Content.Documents.Add(document);
                }
            }
        }//func ExecuteTask

        // ----------------------------------------------------------------------------
        /// <summary>
        ///  Get absolute path of document.
        /// </summary>
        /// <param name="metadata"><see cref="VTFC.MetaData"/> instance.</param>
        /// <returns>Absolute path of the document.</returns>
        // ----------------------------------------------------------------------------
        public static string GetAbsoluteDocumentPath(MetaData metadata)
        {
            lock (olock)
            {
                return Path.Combine(server.WorkDircetory, metadata.Directory.TrimEnd('\\') + "\\" + metadata.FileName);
            }
        }//func GetAbsoluteDocumentPath

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Get absolute path of folder.
        /// </summary>
        /// <param name="folderpath">Folder path.</param>
        /// <returns>Absolute path of the document.</returns>
        // ----------------------------------------------------------------------------
        public static string GetAbsoluteFolderPath(string folderpath)
        {
            lock (olock)
            {
                return Path.Combine(server.WorkDircetory, folderpath);
            }
        }//func GetAbsoluteFolderPath

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Get relative path of the <see cref="VTFC.Document"/> instance.
        /// </summary>
        /// <param name="metadata"><see cref="VTFC.MetaData"/> instance</param>
        /// <returns>Relative path of the document.</returns>
        // ----------------------------------------------------------------------------
        public static string GetRelativeFilePath(MetaData metadata)
        {
            lock (olock)
            {
                return metadata.Directory + "\\" + metadata.FileName;
            }
        }//func GetRelativeFilePath
                                                                                           
        #region New

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Add new document to given folder.
        /// </summary>
        /// <param name="listname">List name.</param>
        /// <param name="folderpath">Folder relative url.</param>
        /// <param name="documentpaths">Document path list.</param>
        // ----------------------------------------------------------------------------
        public static void AddNewDocument(string listname, string folderpath, List<string> documentpaths)
        {
            lock (olock)
            {
                if (documentpaths.Count > 0)
                {
                    try
                    {
                        Dictionary<string, string> documents = new Dictionary<string, string>();
                        foreach (string documentpath in documentpaths)
                        {
                            documents.Add(
                                documentpath,
                                Uri.EscapeUriString(server.Url + "/" + GetUrlFromPath(folderpath + "\\" + Path.GetFileName(documentpath))));
                        }

                        Connect();

                        List<string> updates = UpdateDocuments(listname, documents);
                        
                        string localfolderpath = Path.Combine( server.WorkDircetory, GetPathFromUrl(folderpath));
                        string destinationpath = null;
                        foreach (string sourcepath in documentpaths)
                        {
                            destinationpath = BaseLogic.CopyFile(sourcepath, localfolderpath);
                           BaseLogic.SetReadOnly(destinationpath, true);
                        }

                        UpdateContent(listname, updates);
                    }
                    catch (SoapException ex)
                    {
                        throw new ApplicationException(ex.Detail.InnerText);
                    }
                    finally
                    {
                        Disconnect();
                    }
                }
            }
        }//func AddNewDocument

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Add new SP list.
        /// </summary>
        /// <param name="name">List name.</param>
        /// <param name="description">A string that contains the description for the list.</param>
        /// <param name="enableversioning">Enable versioning for the list.</param>
        /// <param name="quicklaunch">Show list in quick launch.</param>
        /// <remarks>Created <see cref="VTFC.List"/> instance.</remarks>
        // ----------------------------------------------------------------------------
        public static List AddNewList(string name, string description, bool enableversioning, bool quicklaunch)
        {
            lock (olock)
            {
                List list = null;
                try
                {
                    Connect();
                    lists.AddList(name, description, Constant.TFS_DOCUMENTLIBRARY_TEMPLATEID);
                    list = GetLists().Find(l => l.Name.Equals(name));
                    Content.Lists.Add(list);
                    SaveContent();

                    XmlNode listnode = GetNewListQuery(enableversioning, quicklaunch);
                    lists.UpdateList(list.Name, listnode, null, null, null, list.Version);                                        
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return list;
            }
        }//func AddNewList

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Add items in batch to TFS server.
        /// </summary>
        /// <param name="folderpath">Selected path in local workplace.</param>
        /// <param name="listname">List name.</param>
        /// <param name="parentfoldertype">Parent folder type.</param>
        /// <param name="parentrelativefolderpath">Parent folder relative path.</param>
        // ----------------------------------------------------------------------------
        public static void AddItems(string folderpath, string listname, FolderType parentfoldertype, string parentrelativefolderpath)
        {
            lock (olock)
            {
                try
                {
                    Connect();
                    batchcommand = true;

                    DirectoryInfo directory = new DirectoryInfo(folderpath);
                    AddItemRecursive(directory, listname, parentfoldertype, parentrelativefolderpath);
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    batchcommand = false;
                    Disconnect();
                }
            }
        }//func AddItems

        private static void AddItemRecursive(DirectoryInfo directory, string listname, FolderType parentfoldertype, string parentrelativefolderpath)
        {
            string relativefolderpath = null;

            switch (parentfoldertype)
            {
                case FolderType.PROJECT:
                    List list = AddNewList(directory.Name, "", true, true);
                    relativefolderpath = directory.Name;
                    listname = directory.Name;
                    break;
                case FolderType.LIST:
                case FolderType.FOLDER:
                    relativefolderpath = parentrelativefolderpath + "\\" + directory.Name;
                    Folder folder = AddNewFolder(listname, relativefolderpath);                                                        
                    break;
            }

            List<string> files = new List<string>();
            foreach (FileInfo file in directory.GetFiles())
            {
                files.Add(file.FullName);
            }
            if (files.Count > 0)
            {
                AddNewDocument(listname, relativefolderpath, files);
            }    

            foreach (DirectoryInfo subdirectory in directory.GetDirectories())
            {
                AddItemRecursive(subdirectory, listname, FolderType.FOLDER, relativefolderpath);
            }
        }//func AddItemRecursive

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Add new sub folder.
        /// </summary>
        /// <param name="listname">List name.</param>
        /// <param name="relativefolderpath">Relative folder path.</param>
        /// <returns>Created <see cref="VTFC.Folder"/> instance.</returns>
        // ----------------------------------------------------------------------------
        public static Folder AddNewFolder(string listname, string relativefolderpath)
        {
            lock (olock)
            {
                Folder folder = null;
                try
                {
                    string url = GetUrlFromPath(relativefolderpath);
                    Connect();
                    string result = dws.CreateFolder(url);
                    if (result == Constant.TFS_DWS_SUCCESS_RESPONSE)
                    {
                        string folderurl = Uri.EscapeUriString(server.Url + "/" + url);
                        UpdateContent(listname, folderurl);
                        folder = GetFolderByUrl(listname, folderurl);
                    }
                    else
                    {
                        throw new ApplicationException(result);
                    }
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return folder;
            }
        }//func AddNewFolder

        #endregion

        #region Delete

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Delete a list.
        /// </summary>
        /// <param name="listname">Name of the list.</param>
        // ----------------------------------------------------------------------------
        public static void DeleteList(string listname)
        {
            lock (olock)
            {
                try
                {
                    Connect();
                    lists.DeleteList(listname);
                    
                    List list = Content.GetListByName(listname);
                    Content.Lists.Remove(list);
                    
                    SaveContent();
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
            }
        }//func DeleteList

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Delete a folder.
        /// </summary>
        /// <param name="relativefolderpath">Folder path.</param>
        // ----------------------------------------------------------------------------
        public static void DeleteFolder(string relativefolderpath)
        {
            lock (olock)
            {
                try
                {
                    Connect();
                    string result = dws.DeleteFolder(GetUrlFromPath(relativefolderpath));
                    if (result != Constant.TFS_DWS_SUCCESS_RESPONSE)
                    {
                        throw new ApplicationException(result);
                    }
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
            }
        }//func DeleteFolder

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Delete given <see cref="VTFC.Document"/> instance on the server.
        /// </summary>
        /// <param name="document"><see cref="VTFC.Document"/> instance.</param>
        // ----------------------------------------------------------------------------
        public static void DeleteDocument(Document document)
        {
            lock (olock)
            {
                try
                {                   
                    Connect();
                    XmlNode updates = GetUpdateDocumentCommand(Constant.TFS_COMMAND_DELETE, document.Url);
                    lists.UpdateListItems(document.ListName, updates);
                    Content.Documents.Remove(document);
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
            }
        }//func DeleteDocument

        #endregion

        #region Checking
                
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Check out a document.
        /// </summary>
        /// <param name="document"><see cref="VTFC.Document"/> instance.</param>
        /// <returns>Success?</returns>
        // ----------------------------------------------------------------------------
        public static bool CheckOut(Document document)
        {
            lock (olock)
            {
                bool success = false;
                try
                {
                    Connect();
                    success = lists.CheckOutFile(document.Url, "true", "");
                    if (success)
                    {
                        UpdateContent(document.ListName, document.Url);
                        BaseLogic.SetReadOnly(GetAbsoluteDocumentPath(document.MetaData), false);
                    }
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return success;
            }
        }//func CheckOut

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Check in a document.
        /// </summary>
        /// <param name="document"><see cref="VTFC.Document"/> instance.</param>
        /// <returns>Success?</returns>
        // ----------------------------------------------------------------------------
        public static bool CheckIn(Document document)
        {
            bool success = false;
            lock (olock)
            {
                try
                {
                    Connect();

                    Dictionary<string, string> documents = new Dictionary<string, string>();
                    documents.Add(GetAbsoluteDocumentPath(document.MetaData), document.Url);

                    List<string> updates = UpdateDocuments(document.ListName, documents);

                    success = lists.CheckInFile(document.Url, "", ((int)server.DefaultCheckinType).ToString());
                    UpdateContent(document.ListName, updates);
                    BaseLogic.SetReadOnly(GetAbsoluteDocumentPath(document.MetaData), true);
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return success;
            }
        }//func CheckIn

        // ----------------------------------------------------------------------------
        /// <summary>
        /// Undo changes
        /// </summary>
        /// <param name="document"><see cref="VTFC.Document"/> instance.</param>
        /// <returns>Success?</returns>
        // ----------------------------------------------------------------------------
        public static bool UndoPendingChanges(Document document)
        {
            lock (olock)
            {
                bool success = false;
                try
                {
                    Connect();

                    if (document.IsCheckedOut)
                    {
                        success = lists.UndoCheckOut(document.Url);
                    }
                    if (!document.Skipped)
                    {
                        DownloadDocument(document.Url, GetAbsoluteDocumentPath(document.MetaData));
                        UpdateContent(document.ListName, document.Url);
                    }
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
                return success;
            }
        }//func UndoPendingChanges

        #endregion

        #region Rename
                
        // ----------------------------------------------------------------------------
        /// <summary>
        /// Rename a document.
        /// </summary>
        /// <param name="document"><see cref="VTFC.Document"/> instance.</param>
        /// <param name="newname">New FileName of <see cref="VTFC.Document"/> instance.</param>
        // ----------------------------------------------------------------------------
        public static void RenameDocument(Document document, string newname)
        {
            lock (olock)
            {
                try
                {
                    Connect();
                    XmlNode updates = GetRenameListItemCommand(Constant.TFS_COMMAND_UPDATE, Uri.EscapeUriString(document.Url), newname);
                    XmlNode result = lists.UpdateListItems(document.ListName, updates);
                    DataSet ds = GetXmlInDataSet(result);
                    if (ds.Tables["Result"].Rows[0]["ErrorCode"].ToString() != Constant.TFS_LISTS_SUCCESS_RESPONSE)
                    {
                        throw new ApplicationException(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
                    }
                    string newurl = server.Url + "/" + GetUrlFromPath(document.MetaData.Directory) + "/" + newname;

                    Content.Documents.Remove(document);
                    UpdateContent(document.ListName, newurl);                    
                }
                catch (SoapException ex)
                {
                    throw new ApplicationException(ex.Detail.InnerText);
                }
                finally
                {
                    Disconnect();
                }
            }
        }//func RenameDocument

        #endregion

        private static ICredentials GetCredentials()
        {
            ICredentials credentials = null;
            if (server.UseDefaultCredentials)
            {
                credentials = CredentialCache.DefaultCredentials;
            }
            else
            {
                credentials = new NetworkCredential(server.UserName, BaseLogic.DecryptString(server.Password), server.Domain);
            }
            return credentials;
        }//func GetCredentials

        private static void Connect()
        {
            Connect(true, true, true);
        }//func Connect
                        
        private static void Connect( bool clist, bool cdws, bool cug )
        {
            if (!batchcommand)
            {
                ICredentials credentials = GetCredentials();

                if (clist)
                {
                    lists = new Lists();
                    lists.Url = server.Url + Constant.TFS_LISTS_URL;
                    lists.PreAuthenticate = true;
                    lists.Credentials = credentials;
                    lists.Timeout = timeout;
                }
                if (cdws)
                {
                    dws = new Dws();
                    dws.Url = server.Url + Constant.TFS_DWS_URL;
                    dws.PreAuthenticate = true;
                    dws.Credentials = credentials;
                    dws.Timeout = timeout;
                }
                if (cug)
                {
                    usergroup = new UserGroup();
                    usergroup.Url = server.Url + Constant.TFS_USERGROUP_URL;
                    usergroup.PreAuthenticate = true;
                    usergroup.Credentials = credentials;
                    usergroup.Timeout = timeout;
                }
            }
        }//func Connect

        private static void Disconnect()
        {
            if (!batchcommand)
            {
                if (lists != null)
                {
                    lists.Dispose();
                    lists = null;
                }
                if (dws != null)
                {
                    dws.Dispose();
                    dws = null;
                }
                if (usergroup != null)
                {
                    usergroup.Dispose();
                    usergroup = null;
                }
            }
        }//func Disconnect

        private static Content LoadContent(string contentpath)
        {
            Content content = null;
            using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(contentpath)))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(VTFC.Content));
                content = serializer.Deserialize(ms) as Content;
            }
            return content;
        }//func LoadContent

        private static List<List> GetLists()
        {
            List<List> tfslists = new List<List>();

            XmlNode node = lists.GetListCollection();
            DataSet ds = GetXmlInDataSet(node);
            if (ds.Tables.Contains("List"))
            {
                foreach (DataRow row in ds.Tables["List"].Rows)
                {
                    if (row["ServerTemplate"].ToString() == Constant.TFS_DOCUMENTLIBRARY_TEMPLATEID.ToString())
                    {
                        tfslists.Add(new List()
                        {
                            ID = row["Name"].ToString(),
                            Version = row["Version"].ToString(),
                            Name = row["Title"].ToString(),
                            Url = GetDirectoryPath(row["DefaultViewUrl"].ToString().Replace("/Forms/AllItems.aspx", ""), false),
                            Directory = Path.Combine(server.WorkDircetory, row["Title"].ToString()),
                            Folders = new List<Folder>(),
                        });
                    }
                }
            }

            return tfslists;
        }//func GetLists

        private static void GetFolders(List list, string directorypath)
        {
            DataSet ds = GetListContent(list.Name);
            if (ds.Tables.Contains("row")) //a new list does not contain the table named 'row'.
            {
                string path = null;
                string relativepath = null;
                string url = null;
                Document document = null;
                Folder folder = null;
                MetaData metadata = null;
                
                DataRow[] frows = ds.Tables["row"].Select("ows_ContentType='Folder'");
                foreach (DataRow frow in frows)
                {
                    url = frow["ows_EncodedAbsUrl"].ToString();
                    folderurls.Add(url);
                    folder = list.GetFolderByUrl(url);
                    if (folder == null)
                    {
                        folder = new Folder()
                        {
                            Url = url,
                        };
                        list.Folders.Add(folder);
                    }
                    folder.Directory = GetDirectoryPath(frow["ows_FileRef"].ToString(), true);
                }
                                
                DataRow[] drows = ds.Tables["row"].Select("ows_ContentType='Document'");
                foreach (DataRow drow in drows)
                {
                    url = drow["ows_EncodedAbsUrl"].ToString();
                    documenturls.Add(url);
                    document = Content.GetDocumentByUrl(url);
                    if (document == null)
                    {
                        document = new Document()
                        {
                            Url = url,
                            ListName = list.Name,
                        };
                    }

                    metadata = new MetaData();

                    UpdateListItemData(drow, ref document, ref metadata);

                    relativepath = GetRelativeFilePath(metadata);
                    path = GetAbsoluteDocumentPath(metadata);

                    //GetFilePathByUrl method gets relative path of the file
                    if (!File.Exists(path))
                    {
                        AddNewTask(document, path, relativepath, TaskType.DOWNLOAD_AND_CREATE, metadata);
                    }
                    else
                    {
                        if (!BaseLogic.GetReadOnly(path) && !document.IsCheckedOut)
                        {
                            AddNewTask(document, path, relativepath, TaskType.DOWNLOAD_AND_UPDATE, metadata);
                        }
                        else
                        {
                            if (document.MetaData != null)
                            {
                                if (!String.Equals(document.MetaData.Version, metadata.Version))
                                {
                                    AddNewTask(document, path, relativepath, TaskType.COMFIRM_THAN_UPDATE, metadata);
                                }
                            }
                            else
                            {
                                AddNewTask(document, path, relativepath, TaskType.COMFIRM_THAN_UPDATE, metadata);
                            }
                        }
                    }
                }
            }
            CleanFolders(list);
        }//func GetFolders

        private static void UpdateListItemData(DataRow row, ref Document document, ref MetaData metadata)
        {
            int checkedUserID = -1;
            if (Int32.TryParse(GetSharpedText(row["ows_CheckedOutUserId"].ToString()), out checkedUserID))
            {
                document.IsCheckedOut = true;
                document.CheckedOutUserID = checkedUserID;
                document.CheckedOutUserName = GetSharpedText(row["ows_CheckoutUser"].ToString());
            }
            else
            {
                document.IsCheckedOut = false;
                document.CheckedOutUserID = -1;
                document.CheckedOutUserName = null;
            }

            metadata.Directory = GetDirectoryPath(row["ows_FileDirRef"].ToString(), true);
            metadata.FileName = row["ows_LinkFilename"].ToString();
            metadata.Version = row["ows__UIVersionString"].ToString();
            metadata.Modified = DateTime.Parse(row["ows_Modified"].ToString());
            metadata.ModifiedBy = GetSharpedText(row["ows_Editor"].ToString());
            metadata.Size = Int64.Parse(row["ows_FileSizeDisplay"].ToString());
            
            AddIcon(row["ows_DocIcon"].ToString());
        }//func UpdateListItemData

        private static DataSet GetListContent(string listname)
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlNode xmlQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            XmlNode xmlViewFields = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            XmlNode xmlQueryOptions = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
            xmlQueryOptions.InnerXml = "<ViewAttributes Scope='RecursiveAll' IncludeRootFolder='True' />";
            xmlViewFields.InnerXml = "";
            xmlQuery.InnerXml = "";

            XmlNode node = lists.GetListItems(listname, null, xmlQuery, xmlViewFields, Int32.MaxValue.ToString(), xmlQueryOptions, null);
            return GetXmlInDataSet(node);
        }

        private static void AddIcon(string extension)
        {
            if (Content.Icons.Find(i => i.Extension == extension) == null)
            {
                byte[] image = DownloadIcon(String.Format(Constant.TFS_FILEICON_URL, server.Url, extension));
                if (image == null || image.Length == 0)
                {
                    //unknown document icon
                    image = DownloadIcon(String.Format(Constant.TFS_GENICON_URL, server.Url));
                }

                Content.Icons.Add(new Icon()
                {
                    Image = image,
                    Extension = extension,
                });
            }
        }//func AddIcon

        private static byte[] DownloadIcon(string url)
        {
            byte[] icon = null;
            try
            {
                using (WebClient client = new WebClient())
                {
                    client.Credentials = GetCredentials();
                    icon = client.DownloadData(url);
                }
            }
            catch { }
            return icon;
        }//func DownloadData

        private static void DownloadDocument(string url, string documentpath)
        {
            using (WebClient client = new WebClient())
            {
                client.Credentials = GetCredentials();
                BaseLogic.CleanPath(documentpath);
                client.DownloadFile(url, documentpath);
            }
            BaseLogic.SetReadOnly(documentpath, true);
        }//func DownloadDocument
                        
        private static void AddNewTask(Document document, string path, string relativepath, TaskType tasktype, MetaData metadata)
        {
            Task task = new Task()
            {
                Document = document,
                TaskType = tasktype,
                MetaData = metadata,
                Path = path,
                RelativePath = relativepath,
            };
            tasks.Add(task);
        }//func AddNewTask

        private static DataSet GetXmlInDataSet(XmlNode node)
        {
            XmlTextReader reader = new XmlTextReader(node.OuterXml, XmlNodeType.Element, null);

            DataSet ds = new DataSet();
            ds.ReadXml(reader);

            return ds;
        }//GetXmlInDataSet

        private static string GetDirectoryPath(string filedirref, bool sharped)
        {
            if (sharped)
            {
                filedirref = GetSharpedText(filedirref);
            }
            return GetPathFromUrl(filedirref.Replace(GetSiteUrl(server.Url), "").TrimStart('/'));
        }//func GetDirectoryPath

        private static string GetSharpedText(string text)
        {
            return text.Split('#')[1];
        }//func GetSharpedText

        private static string GetListNameFromDirectory(string directory)
        {
            return directory.Split(new char[1] { '\\' }, StringSplitOptions.RemoveEmptyEntries)[0];
        }//func GetListNameFromDirectory

        private static string GetUrlFromPath(string path)
        {
            return path.Replace("\\", "/");
        }//func GetUrlFromPath

        private static string GetPathFromUrl(string url)
        {
            return url.Replace("/", "\\");
        }//func GetPathFromUrl

        private static string GetSiteUrl(string url)
        {
            string siteurl = "";
            string[] parts = url.Split(new char[1] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 2; i < parts.Length; i++)
            {
                siteurl += parts[i] + "/";
            }
            return siteurl.TrimEnd('/');
        }//func GetSiteUrl

        private static XmlNode GetNewListQuery(bool enableversioning, bool quicklaunch)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement ndProperties = doc.CreateElement("List");
            ndProperties.SetAttribute("EnableVersioning", enableversioning.ToString().ToUpper());
            ndProperties.SetAttribute("OnQuickLaunch", quicklaunch.ToString().ToUpper());
            return ndProperties;
        }//func GetNewListQuery

        private static XmlNode GetUpdateDocumentCommand(string command, string documenturl)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement node = doc.CreateElement("Batch");
            node.SetAttribute("OnError", "Continue");
            node.SetAttribute("PreCalc", "TRUE");
            node.InnerXml = String.Format(Constant.TFS_UPDATEDOCUMENT_XML, command, documenturl);
            return node;
        }//func GetUpdateDocumentCommand

        private static XmlNode GetRenameListItemCommand(string command, string documenturl, string newname)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement node = doc.CreateElement("Batch");
            node.SetAttribute("OnError", "Continue");
            node.SetAttribute("PreCalc", "TRUE");
            node.InnerXml = String.Format(Constant.TFS_RENAMELISTITEM_XML, command, documenturl, newname);
            return node;
        }//func GetRenameListItemCommand

        private static string GetCurrentLoginName()
        {
            string loginname = "";
            if (server.UseDefaultCredentials)
            {
                loginname = WindowsIdentity.GetCurrent().Name;
            }
            else
            {
                loginname = server.Domain + "\\" + server.UserName;
            }
            return loginname;
        }//func GetCurrentLoginName
                
        private static DataSet UnescapeListContent(DataSet ds)
        {
            ds.Tables["row"].Columns.Add("ows_UnescapedAbsUrl", typeof(String));
            foreach (DataRow row in ds.Tables["row"].Rows)
            {
                row["ows_UnescapedAbsUrl"] = Uri.UnescapeDataString(row["ows_EncodedAbsUrl"].ToString());
            }
            return ds;
        }//func UnescapeListContent

        private static void UpdateContent(string listname, string update)
        {
            UpdateContent(listname, new List<string> { update });
        }//func UpdateContent

        private static void UpdateContent(string listname, List<string> updates)
        {
            DataSet ds = UnescapeListContent(GetListContent(listname));

            Document document = null;
            List list = null;
            Folder folder = null;
            MetaData metadata = null;
            DataRow row = null;
            string contentType = null;
            string unescapedurl = null;
            string url = null;
            foreach (string update in updates)
            {
                unescapedurl = Uri.UnescapeDataString(update);
                row = ds.Tables["row"].Select(String.Format("ows_UnescapedAbsUrl='{0}'", unescapedurl))[0];
                url = row["ows_EncodedAbsUrl"].ToString();
                contentType = row["ows_ContentType"].ToString();
                switch (contentType)
                {
                    case "Folder":
                        list = Content.GetListByName(listname);
                        folder = list.GetFolderByUrl(url);
                        if (folder == null)
                        {
                            folder = new Folder()
                            {
                                Url = url,
                                Directory = GetDirectoryPath(row["ows_FileRef"].ToString(), true),
                            };
                            list.Folders.Add(folder);
                        }
                        break;
                    case "Document":
                        document = Content.GetDocumentByUrl(url);
                        if (document == null)
                        {
                            document = new Document()
                            {
                                Url = url,
                                ListName = listname,
                            };
                            Content.Documents.Add(document);
                        }
                        metadata = new MetaData();
                        UpdateListItemData(row, ref document, ref metadata);
                        document.MetaData = metadata;
                        break;
                }
            }
            SaveContent();
        }//func UpdateContent

        private static Folder GetFolderByUrl( string listname, string url )
        {
            Folder folder = null;
            List list = Content.GetListByName(listname);
            if (list != null)
            {
                folder = list.GetFolderByUrl(url);

            }
            return folder;
        }//func GetFolderByUrl

        private static List<string> UpdateDocuments(string listname, Dictionary<string, string> documents)
        {
            List<string> updates = new List<string>();
            string documentname = null;
            string documenturl = null;
            byte[] data = null;
            using (WebClient client = new WebClient())
            {
                client.Credentials = GetCredentials();
                foreach (KeyValuePair<string, string> document in documents)
                {
                    documentname = Path.GetFileName(document.Key);
                    data = File.ReadAllBytes(document.Key);
                    documenturl = document.Value;
                    client.UploadData(documenturl, "PUT", data);
                    updates.Add(documenturl);
                }
            }

            XmlNode node = null;
            foreach (string update in updates)
            {
                node = GetUpdateDocumentCommand(Constant.TFS_COMMAND_UPDATE, update);
                lists.UpdateListItems(listname, node);
            }
            return updates;
        }//func UpdateDocuments

        #region Clean

        private static void CleanContent()
        {
            //remove File with metadata null & empty Urls.
            foreach (Document document in Content.Documents.FindAll(f => f.MetaData == null || String.IsNullOrEmpty(f.Url)))
            {
                Content.Documents.Remove(document);
            }
        }//func CleanContent

        private static void CleanLists()
        {
            //checking deleted folders.
            List<List> lists = new List<List>();
            foreach (List list in Content.Lists)
            {
                if (!listurls.Contains(list.Url))
                {
                    lists.Add(list);
                }
            }

            foreach (List dlist in lists)
            {
                Content.Lists.Remove(dlist);
            }

            listurls.Clear();
        }//func CleanLists

        private static void CleanFolders(List list)
        {
            //checking deleted folders.            
            List<Folder> folders = new List<Folder>();
            foreach (Folder folder in list.Folders)
            {
                if (!folderurls.Contains(folder.Url))
                {
                    folders.Add(folder);
                }
            }

            foreach (Folder dfolder in folders)
            {
                list.Folders.Remove(dfolder);
            }
            folderurls.Clear();
        }//func CleanFolders

        private static void CleanDocuments()
        {
            //checking deleted documents.
            List<Document> documents = new List<Document>();
            foreach (Document document in Content.Documents)
            {
                if (!documenturls.Contains(document.Url))
                {
                    documents.Add(document);
                }
            }

            foreach (Document ddocument in documents)
            {
                Content.Documents.Remove(ddocument);
            }

            documenturls.Clear();
        }//func CleanDocuments
        #endregion    
  
    }//class Connector
}//namespace VTFC
